import { IRect } from "../util/Defined";

/**
 * 网格
 */
export class Grid<T = any> {

    /** 所有格子 */
    cells: Set<T>[][] = [];
    /** 物体所在的格子 */
    itemCells = new Map<T, Set<T>[]>();
    /** 格子宽度 */
    cellWidth: number;
    /** 格子高度 */
    cellHeight: number;
    private _maxX: number;
    private _maxY: number;
    private _coverCells: Set<T>[] = [];
    private _coverItems = new Set<T>();

    constructor(
        /** 边界范围 xy零点为左下 */
        public rect: Readonly<IRect>,
        /** x轴格子数量 */
        public col: number,
        /** y轴格子数量 */
        public row: number,
    ) {
        this._maxX = col - 1;
        this._maxY = row - 1;
        this.cellWidth = rect.width / col;
        this.cellHeight = rect.height / row;

        for (let y = 0; y < row; ++y) {
            this.cells[y] = [];
            for (let x = 0; x < col; ++x) {
                this.cells[y][x] = new Set();
            }
        }
    }

    /**
     * 获取矩形覆盖的格子
     * @param rect 矩形范围 xy零点为左下
     * @returns 返回的数组是个只读临时变量，会在下次返回时被修改，不要保持引用
     */
    getCoverCells(rect: Readonly<IRect>): readonly Set<T>[] {
        this._coverCells.length = 0;
        const gx = rect.x - this.rect.x;
        const gy = rect.y - this.rect.y;

        let startX = gx / this.cellWidth >> 0;
        if (startX >= this.col) {
            return this._coverCells;
        } else if (startX < 0) {
            startX = 0;
        }

        let startY = gy / this.cellHeight >> 0;
        if (startY >= this.row) {
            return this._coverCells;
        } else if (startY < 0) {
            startY = 0;
        }

        let endX = ((gx + rect.width) / this.cellWidth) >> 0;
        if (endX < 0) {
            return this._coverCells;
        } else if (endX > this._maxX) {
            endX = this._maxX;
        }

        let endY = ((gy + rect.height) / this.cellHeight) >> 0;
        if (endY < 0) {
            return this._coverCells;
        } else if (endY > this._maxY) {
            endY = this._maxY;
        }

        for (let y = startY; y <= endY; ++y) {
            for (let x = startX; x <= endX; ++x) {
                this._coverCells.push(this.cells[y][x]);
            }
        }

        return this._coverCells;
    }

    /**
     * 添加一个物体
     * @param item 物体
     * @param rect 物体的矩形范围 xy零点为左下
     */
    addItem(item: T, rect: Readonly<IRect>) {
        const cells = this.getCoverCells(rect);
        cells.forEach(cell => {
            cell.add(item);
        });
        this.itemCells.set(item, cells.concat());
    }

    /**
     * 移除物体
     * @param item 物体
     */
    removeItem(item: T) {
        this.itemCells.get(item)?.forEach(cell => {
            cell.delete(item);
        });
        this.itemCells.delete(item);
    }

    /**
     * 更新物体矩形范围
     * @param item 物体
     * @param rect 物体的矩形范围 xy零点为左下
     */
    updateItem(item: T, rect: Readonly<IRect>) {
        this.removeItem(item);
        this.addItem(item, rect);
    }

    /**
     * 获取矩形范围覆盖的所有物体
     * @param rect 矩形范围 xy零点为左下
     * @returns 返回的Set是个只读临时变量，会在下次返回时被修改，不要保持引用
     */
    getCoverItems(rect: Readonly<IRect>) {
        this._coverItems.clear();
        this.getCoverCells(rect).forEach(cell => {
            cell.forEach(item => {
                this._coverItems.add(item);
            });
        })
        return this._coverItems;
    }

    /**
     * 清理网格
     */
    clear() {
        this.cells.forEach(row => {
            row.forEach(cell => {
                cell.clear();
            })
        });
        this.itemCells.clear();
        this._coverCells.length = 0;
        this._coverItems.clear();
    }

}